﻿using System.Collections.Generic;

namespace PlusPlusPaint.FileIO.AST
{
    /// <summary>
    /// Represents a so that contains several sub-nodes.
    /// </summary>
    class CompositeNode : INode
    {
        #region member variables
        string nodeId;
        IList<int> unnamedInts = new List<int>();
        IList<string> unnamedStrings = new List<string>();
        IList<INode> unnamedNodes = new List<INode>();
        IDictionary<string, int> namedInts = new Dictionary<string, int>();
        IDictionary<string, string> namedStrings = new Dictionary<string, string>();
        IDictionary<string, INode> namedNodes = new Dictionary<string, INode>();
        #endregion

        #region constructors
        public CompositeNode(string nodeId)
        {
            this.nodeId = nodeId;
        }

        public CompositeNode(SerializedObject so)
        {
            this.nodeId = so.Name;
            this.unnamedInts = so.UnnamedInts;
            this.unnamedStrings = so.UnnamedStrings;
            foreach (SerializedObject subobject in so.UnnamedSubobjects)
                this.unnamedNodes.Add(new CompositeNode(subobject));
            this.namedInts = so.NamedInts;
            this.namedStrings = so.NamedStrings;
            foreach (KeyValuePair<string, SerializedObject> subobject in so.NamedSubobjects)
                this.namedNodes[subobject.Key] = new CompositeNode(subobject.Value);
        }
        #endregion

        #region overrides
        public override string ToString()
        {
            string contents = ContentsToString();
            bool hasContents = contents.Trim() != "";
            return "(" + nodeId + (hasContents ? " " + contents : "") + ")";
        }
        #endregion

        #region public methods
        public string ToPrettyString()
        {
            string contents = INodeHelpers.Indent(ContentsToPrettyString(), INodeHelpers.PrettyStringIndent);
            bool hasContents = contents.Trim() != "";
            return "(" + nodeId + (hasContents ? "\r\n" + contents + "\r\n" : "") + ")";
        }

        public SerializedObject ToSerializedObject()
        {
            // Shallow copy; may be problematic, but can fix later.  Don't
            // write code that depends on it. 
            IList<SerializedObject> unnamedSubobjects = new List<SerializedObject>();
            foreach (INode node in this.unnamedNodes)
                unnamedSubobjects.Add(node.ToSerializedObject());
            IDictionary<string, SerializedObject> namedSubobjects = new Dictionary<string, SerializedObject>();
            foreach (KeyValuePair<string, INode> kvp in this.namedNodes)
                namedSubobjects[kvp.Key] = kvp.Value.ToSerializedObject();
            return new SerializedObject( 
                this.nodeId,
                this.unnamedInts,
                this.unnamedStrings,
                unnamedSubobjects,
                this.namedInts,
                this.namedStrings,
                namedSubobjects
            );
        }

        public void AddArg(string name, int i)
        {
            namedInts[name] = i;
        }

        public void AddArg(string name, string str)
        {
            namedStrings[name] = str;
        }

        public void AddArg(string name, INode node)
        {
            namedNodes[name] = node;
        }

        public void AddArg(int i)
        {
            unnamedInts.Add(i);
        }

        public void AddArg(string str)
        {
            unnamedStrings.Add(str);
        }

        public void AddArg(INode node)
        {
            unnamedNodes.Add(node);
        }

        public IList<Layers.ILayer> ExplodeAsLayers()
        {
            return new List<Layers.ILayer>();
        }
        #endregion

        #region private methods
        string ContentsToString()
        {
            string result = "";
            foreach (int i in unnamedInts)
                result += i + " ";
            foreach (string str in unnamedStrings)
                result += LayeredDataLexer.SerializeString(str) + " ";
            foreach (INode node in unnamedNodes)
                result += node + " ";
            foreach (KeyValuePair<string, int> kvp in namedInts)
                result += kvp.Key + ":" + kvp.Value + " ";
            foreach (KeyValuePair<string, string> kvp in namedStrings)
                result += kvp.Key + ":" + LayeredDataLexer.SerializeString(kvp.Value) + " ";
            foreach (KeyValuePair<string, INode> kvp in namedNodes)
                result += kvp.Key + ":" + kvp.Value + ' ';
            return result.TrimEnd();
        }

        string ContentsToPrettyString()
        {
            string result = "";
            foreach (int i in unnamedInts)
                result += i + "\r\n";
            foreach (string str in unnamedStrings)
                result += LayeredDataLexer.SerializeString(str) +"\r\n";
            foreach (INode node in unnamedNodes)
                result +=  node.ToPrettyString()+ "\r\n";
            foreach (KeyValuePair<string, int> kvp in namedInts)
                result += kvp.Key + ":\r\n" + INodeHelpers.Indent(kvp.Value.ToString(), INodeHelpers.PrettyStringIndent) + "\r\n";
            foreach (KeyValuePair<string, string> kvp in namedStrings)
                result += kvp.Key + ":\r\n" + INodeHelpers.Indent(LayeredDataLexer.SerializeString(kvp.Value), INodeHelpers.PrettyStringIndent) + "\r\n";
            foreach (KeyValuePair<string, INode> kvp in namedNodes)
                result += kvp.Key + ":\r\n" + INodeHelpers.Indent(kvp.Value.ToPrettyString(), INodeHelpers.PrettyStringIndent) + "\r\n";
            return result.TrimEnd();
        }
        #endregion
    }
}
